import pandas as pd
import numpy as np
from scipy import stats
import scipy.stats as stats
import matplotlib.pyplot as plt
from scipy.cluster.hierarchy import linkage, dendrogram  # linkage 计算数据点之间距离的函数，dendrogram画树状图函数
from scipy.cluster.hierarchy import fcluster
from sklearn.preprocessing import normalize   # 对数据做归一化，防止过大的数据对距离的计算产生过大的影响，基本上涉及到计算距离的到要做
from sklearn.cluster import KMeans
from pandas.plotting import scatter_matrix   # 画特征之间相关性的矩阵
from sklearn.preprocessing import StandardScaler  # 数据标准化
from sklearn import metrics  # 计算轮廓系数,判定聚类效果
import missingno as msno # 观测缺失数据特别好的模块

'''层级聚类,聚类成树形图'''
def tree1():
    seeds_df = pd.read_csv('E:\培训教程\python\机器学习数学基础\统计分析\聚类分析\datasets\seeds-less-rows.csv')
    seeds_df = seeds_df.head()
    print(seeds_df)
    '''从df中弹出一列，并将其装换成列表'''
    varieties = list(seeds_df.pop('grain_variety'))
    '''seeds_df.values 将df中的数据取出，转换成numpy格式的'''
    samples = seeds_df.values
    # 进行层次聚类
    mergings = linkage(samples, method='complete')
    fig = plt.figure(figsize=(10, 6))
    '''画树状图 ，mergings 层次聚类的数据，labels为树状图叶子节点的标签'''
    dendrogram(mergings,labels=varieties,leaf_rotation=90,leaf_font_size=6)
    plt.show()

def tree2():
    scores_df = pd.read_csv('E:\培训教程\python\机器学习数学基础\统计分析\聚类分析\datasets\eurovision-2016-televoting.csv', index_col=0)

    '''观测数据集中的缺失值，其中白色条带为缺失值'''
    msno.matrix(scores_df)
    plt.show()

    country_names = list(scores_df.index)
    '''缺失值填充，没有的就先按满分算吧'''
    scores_df = scores_df.fillna(12)
    '''先取出df中的数据，并转换为np,在对数据做归一化处理'''
    samples = normalize(scores_df.values)


    '''
    使用两种不同的距离画出来的柱状图完全不同
    linkage 第一个参数为列表，归一化之后的列表，第二个参数为使用的距离，是最近，最远还是平均'''
    mergings = linkage(samples, method='single')
    fig = plt.figure(figsize=(10, 6))
    dendrogram(mergings,labels=country_names,leaf_rotation=90,leaf_font_size=6)
    plt.show()

    merging = linkage(samples, method='complete')
    fig = plt.figure(figsize=(10, 6))
    dendrogram(merging, labels=country_names, leaf_rotation=90, leaf_font_size=6)
    plt.show()


'''K-means clustering'''
def k_means():

    beer = pd.read_csv('E:\培训教程\python\唐宇迪-机器学习课程\机器学习算法配套案例实战\聚类算法\聚类算法\data.txt', sep=' ')
    X = beer[["calories", "sodium", "alcohol", "cost"]]
    '''数据标准化和归一化处理'''

    run_Scaled_data(beer, X)


    '''使用kmeans算法对数据进行聚类，聚类结果为3簇和2簇'''
    km = KMeans(n_clusters=3).fit(X)
    km2 = KMeans(n_clusters=2).fit(X)

    '''输出数据的聚类结果，labels_ 为数据对应的类,将聚类的记过添加到原始数据集上'''
    beer['cluster'] = km.labels_
    beer['cluster2'] = km2.labels_
    print(beer.sort_values('cluster'))

    '''分别取两次聚类的中心点,也就是质心点坐标'''
    cluster_centers = km.cluster_centers_
    cluster_centers_2 = km2.cluster_centers_

    print('cluster_centers' , cluster_centers)
    print('cluster_centers_2' ,cluster_centers_2)

    '''按照聚类结果分组，查看原始数据中的数据均值，观察分类情况和那个值的变化最相关'''
    print(beer.groupby("cluster").mean())
    print(beer.groupby("cluster2").mean())

    centers = beer.groupby("cluster").mean().reset_index()
    colors = np.array(['red', 'green', 'blue', 'yellow'])
    plt.scatter(beer["calories"], beer["alcohol"], c=colors[beer["cluster"]])
    plt.scatter(centers.calories, centers.alcohol, linewidths=3, marker='+', s=300, c='black')
    plt.xlabel("Calories")
    plt.ylabel("Alcohol")

    '''画散点图矩阵，两两变量之间的散点图关系'''
    scatter_matrix(beer[["calories", "sodium", "alcohol", "cost"]], s=100, alpha=1, c=colors[beer["cluster"]],figsize=(10, 10))
    plt.suptitle("With 3 centroids initialized")
    plt.show()

    '''计算聚类效果的轮廓系数'''
    silhouette_score(beer, X)

def run_Scaled_data(beer,X):
    scaler = StandardScaler()
    '''实例化一个标准化对象，对数据X做标准化'''
    X_scaled = scaler.fit_transform(X)

    km = KMeans(n_clusters=3).fit(X_scaled)
    beer["scaled_cluster"] = km.labels_
    print(beer.sort_values("scaled_cluster"))

    '''以聚类结果分组，查看原始数据的均值情况'''
    colors = np.array(['red', 'green', 'blue', 'yellow'])
    print(beer.groupby("scaled_cluster").mean())
    scatter_matrix(X, c=colors[beer.scaled_cluster], alpha=1, figsize=(10, 10), s=100)
    plt.show()

def silhouette_score(beer,X):

    '''使用标准化之后和标准化之前的数据分别计算轮廓系数，一般情况下，使用标准化之后的数据聚类效果会比没有标准化的数据聚类效果好很多，
    但是如果某一个特征本身就对结果影响非常大，并且在数据中这个特征的值也很大，则在标准化之后，数据的聚类效果可能会变差
    '''
    score_scaled = metrics.silhouette_score(X, beer.scaled_cluster)
    score = metrics.silhouette_score(X, beer.cluster)
    print('The score_scaled silhouette_score is ',score_scaled)
    print('The score silhouette_score is ', score)

    '''使用k-means聚类时，不知道聚为几类时，遍历一下参数的可能值，计算轮廓系数，取最高的轮廓系数对应的簇数'''
    scores = []
    for k in range(2, 20):
        labels = KMeans(n_clusters=k).fit(X).labels_
        score = metrics.silhouette_score(X, labels)
        scores.append(score)
    print('The silhouette_score_array is ',scores)


if __name__ == '__main__':
    tree2()